/* 
 * File:   User.cpp
 * Author: user
 * 
 * Created on July 23, 2014, 1:29 AM
 */

#include <conio.h>
#include "User.h"
#include "ExamTopic.h"

const string sql_DB = "CSCI222_A2.db";
const string Login_table = "LoginAccount";
const string ExamTopic_table = "ExamTopic";

static bool _header;
static bool _displayCallBack;
static bool _accountFound;
static string _queryUserID;

//#define _debug

User::User() 
{
    _header = false;
    _displayCallBack = false;
    _userID = "";
    _userType = INVALID;
}

User::User(string userID,int userType)
{
    _header = false;
    _displayCallBack = false;
    _userID = userID;
    _userType = userType;
}

User::User(const User& orig) 
{
}

User::~User() 
{
}

string User::getUserID()
{
    return _userID;
}

string User::getLoginID()
{
    return _loginAccount.getLoginID();
}

int User::login()
{
    system("clear");
    cout << "[-----Login account-----]" << endl;
    int loginOK(-1);
    int count(0);
    while(loginOK==-1 && count < 3 )
    {
        string loginID = "";
        string password = "";
        cout << "Please enter Login ID: ";
        getline(cin,loginID);
        cout << "Please enter password: ";        
        password = getUserPassword(); 
        cout << endl;
        count++;

        string createBy;
        loginOK = _loginController.verifyLogin(loginID,password,_userType,_userID,createBy); 
        if (loginOK >0)
        {
            _loginAccount.setLoginID(loginID);
            _loginAccount.setPassword(password);
            _loginAccount.setCreateBy(createBy);
        }

        if (loginOK < 0)
            cout << "Wrong LoginID or Password. Please try again." << endl <<endl;
        else
            cout << "Login successfully!!!" << endl;
    }
    
    return loginOK;
}

/*
 OFF console terminal echo and restore back after get the input char
 */
string User::getUserPassword() 
{
    struct termios oldt,
                   newt;
    int            ch;
    string pwd("");
    tcgetattr( STDIN_FILENO, &oldt );
    newt = oldt;
    //Turn echo off
    newt.c_lflag &= ~( ICANON | ECHO );
    tcsetattr( STDIN_FILENO, TCSANOW, &newt );
    ch = getchar();
    while(ch != 10)//character 10 is enter
    {   
        if (ch == 127) // backspace
        {
            if (!pwd.empty())
            {
                cout << '\b' << ' ' << '\b';
                pwd = pwd.substr(0,pwd.length()-1);
            }
        }
        else
        {
            pwd.push_back(ch);
            cout << '*';
        }
       //Get input
       ch = getchar();
    }
    //Restore terminal
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
    return pwd;
}

bool User::addAccount(string userID,string loginID, string loginPass,int userType, string createBy)
{
    if (userID.empty() || loginID.empty() || loginPass.empty() || _userID.empty() || _userType+1 < 0)
    {
        cout << endl << endl << "Add account failed!!!" << endl;
        cout << "UserID , LoginID and password field can't be empty" << endl;
        cout << "Add account failed!!!" << endl;
        return false;
    }
    //encrypt password 
    _loginController.processPassword(loginPass);
    
    char * sqlInsert;
    sqlInsert = sqlite3_mprintf("INSERT INTO %q (UserID,LoginID,LoginPassword,AccountType,CreateBy)" \
                  "VALUES ('%q', '%q', '%q', '%d','%q');",
                  Login_table.c_str(), userID.c_str(),loginID.c_str(),loginPass.c_str(),userType, createBy.c_str());
    
    
    if (executeSql(sqlInsert))
        cout << endl << "Add account successfully!!!" << endl;
    else
    {
        cout << "Add account failed!!!" << endl;
        return false;
    }
    return true;
}

bool User::deleteAccount(string index)
{
    _accountFound = false;
    _displayCallBack = false;
    char * sqlAuth;
    sqlAuth = sqlite3_mprintf("SELECT * FROM %q WHERE ID='%q' AND CreateBy='%q' AND AccountType='%d'", 
            Login_table.c_str(), index.c_str(), _userID.c_str(), _userType+1); 
    executeSql(sqlAuth);
    if(!_accountFound)
    {
        cout << endl << "Account not found in database!!!" << endl; 
        cout << "Delete account failed!!!" << endl;
        return false;
    }
        
    char * sqlDel;
    sqlDel = sqlite3_mprintf("DELETE FROM %q WHERE ID='%q' AND AccountType='%d' AND CreateBy='%q';" ,
                  Login_table.c_str(), index.c_str(),_userType+1,_userID.c_str()); 
    
    if (executeSql(sqlDel))
        cout << endl << "Account deleted successfully!!!" << endl; 
    else
    {
        cout << "Delete account failed!!!" << endl;
        return false;
    }
    
    return true;  
}

bool User::updateAccount(string index, string newUserID)
{
    _accountFound = false;
    _displayCallBack = false;
    char * sql;
    sql = sqlite3_mprintf("SELECT * FROM %q WHERE ID='%q' AND CreateBy='%q'", 
            Login_table.c_str(), index.c_str(), _userID.c_str()); 
    executeSql(sql);
    if(!_accountFound)
    {
        cout << endl << "Account not found in database!!!" << endl; 
        cout << "update account failed!!!" << endl;
        return false;
    }
        
    char * sqlUpdate;
    sqlUpdate = sqlite3_mprintf("UPDATE %q set UserID = '%q' WHERE CreateBy='%q'AND ID=='%q';",
                  Login_table.c_str(), newUserID.c_str(),_userID.c_str(),index.c_str()); 
    
    if (executeSql(sqlUpdate))
        cout << endl << "update account successfully!!!" << endl; 
    else
    {
        cout << endl << "update account failed!!!" << endl;
        return false;
    }
    
    //update the the account created by the affected account
    char * sqlUpdate2;
    sqlUpdate2 = sqlite3_mprintf("UPDATE %q set CreateBy = '%q' WHERE CreateBy='%q'",
                  Login_table.c_str(), newUserID.c_str(),_queryUserID.c_str()); 
    
    if (executeSql(sqlUpdate2))
        cout << "update affected account successfully!!!" << endl; 
    else
    {
        cout << "update account failed!!!" << endl;
        return false;
    }
    
    //update the the exam topic created by the affected account
    char * sqlUpdate3;
    sqlUpdate3 = sqlite3_mprintf("UPDATE %q set CreateBy = '%q' WHERE CreateBy='%q'",
                  ExamTopic_table.c_str(), newUserID.c_str(),_queryUserID.c_str()); 
    
    if (executeSql(sqlUpdate3))
        cout << "update exam Topic successfully!!!" << endl; 
    else
    {
        cout << "update account failed!!!" << endl;
        return false;
    }
    
    return true;
}

void User::displayAllUserAccount(string createBy)
{ 
    system("clear");
    cout << "[-----All accounts-----]"<< endl;
    _displayCallBack = true;
    _header = true;
    char * sql;
    if (createBy == "")
        sql = sqlite3_mprintf("SELECT * FROM %q;",Login_table.c_str()); 
    else
        sql = sqlite3_mprintf("SELECT * FROM %q WHERE CreateBy='%q';",
                Login_table.c_str(),createBy.c_str()); 
    executeSql(sql);
    
    _displayCallBack = false;
    _header = false;
}

bool User::executeSql(const char* sql) 
{
   sqlite3 *db;
   char *zErrMsg = 0;
   int rc;
   /* Open database */
   rc = sqlite3_open(sql_DB.c_str(), &db); 
   
   if( rc )
   {
      fprintf(stderr, "\n\nCan't open database: %s\n", sqlite3_errmsg(db));
      return false;
   }else{
      //fprintf(stderr, "Opened database successfully\n");
   }
           
   /* Execute SQL statement */
   rc = sqlite3_exec(db, sql, querryCallBack, 0, &zErrMsg);   
   
   if( rc != SQLITE_OK )
   {
      fprintf(stderr, "\n\nSQL error: %s\n", zErrMsg);
      sqlite3_free(zErrMsg);
      return false;
   }else{
      //fprintf(stdout, "retrieve data successfully\n");
   }
 
   return true;
   sqlite3_close(db);
}

int User::querryCallBack(void *data, int argc, char **argv, char **azColName)
{
   if(_displayCallBack)
   {
        if (_header)
        {
         _header = false;
         cout << left << setw(10)
         << "Index" << left << setw(20)
         << "UserID" << left << setw(20)
         << "LoginID" << left << setw(20)  
         << "Password" << left << setw(15) 
         << "AccountType" << left << setw(20)
         << "CreateBy";        
   
#ifdef _debug
    cout << left << setw(20)
         << "Password" << endl;
    cout << "--------------------------------------------------------------------------------------" << endl;
#else
    cout << endl;
    cout << "---------------------------------------------------------------------------------------" << endl;
#endif
        }
        cout << left << setw(10)
             << (argv[0] ? argv[0] : "NULL") << left << setw(20) 
             << (argv[1] ? argv[1] : "NULL") << left << setw(20) 
             << (argv[2] ? argv[2] : "NULL") << left << setw(20)  
             << (argv[3] ? argv[3] : "NULL") << left << setw(15) 
             << (argv[4] ? argv[4] : "NULL") << left << setw(20)
             << (argv[5] ? argv[5] : "NULL");
    
#ifdef _debug
    //decode password
    string pass = argv[3];
    LoginController::processPassword(pass);
    cout << left << setw(20)
         << pass << endl;
#else
    cout << endl;
#endif

   }
   _queryUserID = (argv[1] ? argv[1] : "NULL");
   _accountFound = true; 
   return 0;
}

bool User::validateIntInput(string input, int& output)
{
    for (int i=0;i<input.length();i++)
    {
        if (!isdigit(input[0])) 
        {
            cout << "Invalid input. Please try again" << endl;
            return false;
        }
    }

    stringstream ss (input);
    ss >> output;
    
    return true;
}

void User::menuItemAction(menuItemType mItemType,bool& isExitProgram, int& loginStatus)
{
    system("clear");
    switch(mItemType)
    {
        case LOG_OUT:
        {
            cout << "[-----Log out-----]"<< endl;
            cout << "Are you sure want to log out? (Y/N): ";
            string ans;
            //cin.clear ();
            //cin.ignore (100, '\n');
            getline(cin, ans);
            while(ans!="Y" && ans != "N")
            {
                cout << "Invalid input!!!" << endl;
                cout << "Are you sure want to log out? (Y/N): ";
                getline(cin, ans);
            }
            if (ans == "Y")
               loginStatus = -1;
            break;
        }
        case ADD_ACCT:
        {
            cout << "[-----Add account-----]"<< endl;
            cout << "Please enter new account UserID: ";
            string newUserID;
            //cin.clear ();
            //cin.ignore (100, '\n');
            getline(cin, newUserID);
            
            cout << "Please enter new account LoginID: ";
            string newLoginID;
            getline(cin, newLoginID);
            
            cout << "Please enter new account password: ";
            string password;
            password = getUserPassword(); 
            
            addAccount(newUserID,newLoginID, password,_userType+1,_userID);  
            break;
        }
        case UPDATE_ACCT:
        {
            cout << "[-----UPDATE account-----]"<< endl;
            displayAllUserAccount(_userID);
            cout << endl << "Please enter a account index: ";
            string index;
            //cin.clear ();
            //cin.ignore (100, '\n');
            getline(cin, index);
            
            cout << "Please enter new UserID: ";
            string newUserID;
            getline(cin, newUserID);
            
            updateAccount(index,newUserID);
            break;
        }
        case DEL_ACCT:
        {
            cout << "[-----Delete account-----]"<< endl;
            displayAllUserAccount(_userID);
            cout << endl << "Please enter a account index: ";
            string index;
            //cin.clear ();
            //cin.ignore (100, '\n');
            getline(cin, index);
            
            deleteAccount(index);  
            break;
        }
        case QUIT_PROGRAM:
        {
            cout << "[-----Quit program-----]" << endl;
            cout << "Are you sure want to exit? (Y/N): ";
            string ans;
            //cin.clear ();
            //cin.ignore (100, '\n');
            getline(cin, ans);
            while(ans!="Y" && ans != "N")
            {
                cout << "Invalid input!!!" << endl;
                cout << "Are you sure want to exit? (Y/N): ";
                getline(cin, ans);
            }
            if (ans == "Y")
                isExitProgram = true;
        }
    }
}

